<?php

namespace App\Kuafu\Base\Model;

use App\Exceptions\BaseException;
use App\Kuafu\Base\Enum\BaseCodeEnum;
use App\Kuafu\Pay\Model\PayStreamModel;
use App\Kuafu\Shop\Enum\ShopCodeEnum;
use App\Kuafu\Shop\Exceptions\ShopException;
use Closure;
use DateTimeInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\HigherOrderTapProxy;

class BaseModel extends Eloquent
{
    protected $connection = 'mysql';
    public    $timestamps = true;
    public const CREATED_AT = 'createdAt';
    public const UPDATED_AT = 'updatedAt';
    protected $dateFormat = 'U';

    const DELETE_YES = 1;
    const DELETE_NO  = 0;

    // 通用开关 开 [仅限 1 和 0 的情况下使用]
    const SWITCH_ON = 1;

    // 通用开关 关 [仅限 1 和 0 的情况下使用]
    const SWITCH_OFF = 0;


    /**
     * 标记下Model是否加了缓存 - 给开发看
     * = true 的情况，需要在增删改的Service层做Cache::forget
     * @var bool
     */
    protected $modelCached = false;

    public static function returnData(int $code, string $msg = '', array $data = null)
    {
        if (!$msg) {
            $msg = BaseCodeEnum::CODE_TEXT[$code] ?? '';
        }

        return ['code' => $code, 'msg' => $msg, 'data' => $data ?: null];
    }

    protected function serializeDate(DateTimeInterface $date)
    {
        return $date->format('Y-m-d H:i:s');
    }

    protected static ?int $splitShopId;
    protected static int  $sysType = PayStreamModel::SYS_TYPE_RMS_MEAL;

    /**
     * 设置分表用的shopId
     * @param int $shopId
     * @return $this
     */
    public function setSplitShopId(?int $shopId): self
    {
        self::$splitShopId = $shopId;
        return $this;
    }

    /**
     * 设置分表用的sysType
     * @param int $sysType
     * @return $this
     */
    public function setSysType(int $sysType): self
    {
        self::$sysType = $sysType;
        return $this;
    }

    /**
     * 根据shopId分表的规则
     * @return string
     */
    protected function getSuffixByShopId(): string
    {
        return '_' . str_pad(self::$splitShopId % 100, 3, 0, 0);
    }

    /**
     * @param array $conditions
     * @param $init
     * @param Closure|null $afterCreateClosure
     * @return Builder|Eloquent|HigherOrderTapProxy|mixed|object|null
     */
    public static function customFirstOrCreate(array $conditions, $init = null, Closure $afterCreateClosure = null)
    {
        if (!is_null($instance = self::query()->where($conditions)->first())) {
            return $instance;
        }

        return tap(static::query()->newModelInstance(
            array_merge(
                $conditions,
                $init ? ($init instanceof Closure ? $init() : $init) : []
            )
        ), function ($instance) use ($afterCreateClosure) {
            $instance->save();

            if ($afterCreateClosure) {
                $afterCreateClosure($instance);
            }
        });
    }

    /**
     * 从 redis => db 获取自增 ID
     *
     * @return int
     * @throws ShopException
     */
    public function maxId(): int
    {
        try {
            $redis   = Redis::connection();
            $isRedis = true;
        } catch (\Exception $e) {
            $isRedis = false;
        }

        if ($isRedis) {
            $tableName = $this->getTable();
            $key       = vsprintf('AUTO-INCR-MAX-ID:%s', [$tableName]);
            $lockKey   = vsprintf('AUTO-INCR-MAX-LOCK:%s', [$tableName]);

            $maxId = $redis->get($key);
            if (!empty($maxId)) {
                // 上锁
                if (!$redis->setnx($lockKey, time())) {
                    // 防止死锁
                    $lock_time = $redis->get($lockKey);
                    if (($lock_time + 5) < time()) {
                        $redis->del($lockKey);
                    } else {
                        throw new ShopException(ShopCodeEnum::ERR_SHOP_LOCK);
                    }
                }
                // 自增ID
                $maxId = $redis->incr($key);
                // 如果存在 直接从数据库中获取最大key
                if ($this->checkIdExist($maxId)) {
                    $maxId = $this->getMaxId() + 1;
                }
            } else {
                $maxId = $this->getMaxId() + 1;
            }
            $redis->set($key, $maxId);
            $redis->del($lockKey);
        } else {
            $maxId = $this->getMaxId() + 1;
        }

        return $maxId;
    }

}
